﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using ZombZomb.GameObjects;

namespace ZombZomb.GameObjects
{
    public class GameSprite
    {

        #region Fields

        /// <summary>
        /// Position du sprite (coordonnées !! CENTRE !! )
        /// </summary>
        private Vector2 position;
        

        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }

        /// <summary>
        /// Indique si le sprite doit être inversé
        /// </summary>
        private bool flip = false;

        public bool Flip
        {
            get { return flip; }
            set { flip = value; }
        }

        private bool flipV = false;

        public bool FlipV
        {
            get { return flipV; }
            set { flipV = value; }
        }

        /// <summary>
        /// L'image du sprite. Image simple ou table de sprites
        /// </summary>
        private Texture2D image;

        public Texture2D Image
        {
            get { return image; }
            set { image = value; }
        }

        /// <summary>
        /// Indique si le GameSprite est un sprite animé (spriteSheet true) ou une simple image (false)
        /// </summary>
        private bool spriteSheet;

        public bool IsSpriteSheet
        {
            get { return spriteSheet; }
            set { spriteSheet = value; }
        }


        /// <summary>
        /// Rotation du sprite
        /// </summary>
        private float spriteRotation = 0.0f;

        public float SpriteRotation
        {
            get { return spriteRotation; }
            set { spriteRotation = value; }
        }

        /// <summary>
        /// L'origine du sprite, centre de l'image à afficher.
        /// Centre de l'image si sprite simple, centre de la frame d'affichage si animé
        /// </summary>
        private Vector2 spriteOrigin = Vector2.Zero;

        public Vector2 SpriteOrigin
        {
            get { return spriteOrigin; }
            set { spriteOrigin = value; }
        }

        /// <summary>
        /// Par défaut = blanc = rien, mais on peut colorer le sprite d'une autre couleur si on le désire
        /// </summary>
        private Color colorTint = Color.White;

        public Color ColorTint
        {
            get { return colorTint; }
            set { colorTint = value; }
        }

        /// <summary>
        /// The sorting depth of the sprite, between 0 (front) and 1 (back).
        /// </summary>
        private float layerDepth = 0.0f;

        public float LayerDepth
        {
            get { return layerDepth; }
            set { layerDepth = value; }
        }



        /// <summary>
        /// Echelle d'affichage
        /// </summary>
        private float scale = 1.0f;

        public float Scale
        {
            get { return scale; }
            set { scale = value; }
        }

        private bool animationImmobile;
        public bool AnimationImmobile
        {
            get { return animationImmobile; }
            set { animationImmobile = value; }
        }

        #endregion

        #region Fields Animation

        /// <summary>
        /// Le dictionnaire contient toutes les animations de l'objet
        /// par exemple "course" et "saut" sont 2 animations différentes(mais issues d'une meme spriteSheet)
        /// Exemple quelconque d'utilisation d'un dictionnaire :
        /// Dictionary<string, integer> ScreenWidths = new Dictionary<string, integer>();
        /// ScreenWidths.Add("PC", 1600);
        /// ScreenWidths.Add("XBOX", 640);
        /// ScreenWidths.Add("XBOX360", 1280);
        /// We could access these values by using their unique key, so:
        /// X=ScreenWidths("XBOX360");
        /// </summary>
        Dictionary<string, FrameAnimation> faAnimations = new Dictionary<string, FrameAnimation>();
        
        internal Dictionary<string, FrameAnimation> FaAnimations
        {
            get { return faAnimations; }
            set { faAnimations = value; }
        }

        ///<summary>
        ///The FrameAnimation object of the currently playing animation
        ///</summary>
        public FrameAnimation CurrentFrameAnimation
        {
            get
            {
                if (!string.IsNullOrEmpty(currentAnimation))
                    return faAnimations[currentAnimation];
                else
                    return null;
            }
        }

        
        /// <summary>
        /// Nom de l'animation courrante
        /// </summary>
        string currentAnimation = null;

        /// <summary>
        /// Donne le nom de l'animation courante.
        /// Setting the animation resets the CurrentFrame and PlayCount properties to zero.
        /// </summary>
        public string CurrentAnimation
        {
            get { return currentAnimation; }
            set
            {
                if (faAnimations.ContainsKey(value))
                {
                    currentAnimation = value;
                    faAnimations[currentAnimation].CurrentFrame = 0;
                    faAnimations[currentAnimation].PlayCount = 0;
                }
            }
        }

        /// <summary>
        /// Fixe la largeur du cadre de lecture de la table de sprite
        /// </summary>
        int spriteWidth;

        public int SpriteWidth
        {
            get { return spriteWidth; }
            set { spriteWidth = value; }
        }

        /// <summary>
        /// Fixe la hauteur du cadre de lecture de la table de sprite
        /// </summary>
        int spriteHeight;

        public int SpriteHeight
        {
            get { return spriteHeight; }
            set { spriteHeight = value; }
        }

        /// <summary>
        /// True if the sprite is (or should be) playing animation frames.  If this value is set
        /// to false, the sprite will not be drawn (a sprite needs at least 1 single frame animation
        /// in order to be displayed.
        /// </summary>
        bool animating = false;

        public bool IsAnimating
        {
            get { return animating; }
            set { animating = value; }
        }

        public void SetXPosition(float x)
        {
            position.X = x;
        }

        public void SetYPosition(float y)
        {
            position.Y = y;
        }


        #endregion

        #region Initialize

        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="xy">position du sprite à l'affichage. Relative au CENTRE de l'image</param>
        /// <param name="texture">l'image à utiliser pour le sprite</param>
        /// <param name="isSpriteSheet">Indique si l'image est un sprite simple ou une table de sprites</param>
        public GameSprite(Texture2D texture, Vector2 xy, bool isSpriteSheet)
        {
            position = xy;
            image = texture;
            spriteSheet = isSpriteSheet;

            //this.particleEngine = new ParticleEngine
            //si l'image est un sprite simple, ses propriétés sont fixes et on peut les initialiser
            //Sinon, elles seront modifiées à chaque changement d'animation
            if (!IsSpriteSheet)
            {
                spriteWidth = image.Width;
                spriteHeight = image.Height;
                spriteOrigin = new Vector2(image.Width / 2, image.Height / 2);
            }
                
        }

        public GameSprite(Texture2D texture, Vector2 xy, bool isSpriteSheet, bool animationImmobile)
        {
            position = xy;
            image = texture;
            spriteSheet = isSpriteSheet;
            //si l'image est un sprite simple, ses propriétés sont fixes et on peut les initialiser
            //Sinon, elles seront modifiées à chaque changement d'animation
            if (!IsSpriteSheet)
            {
                spriteWidth = image.Width;
                spriteHeight = image.Height;
                spriteOrigin = new Vector2(image.Width / 2, image.Height / 2);
            }
            animating = true;
            this.AnimationImmobile = animationImmobile;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Ajoute une animation à l'objet en se basant sur sa spriteSheet
        /// </summary>
        /// <param name="Name">Nom de l'animation</param>
        /// <param name="X">position (en pxl) X de la première frame de l'animation</param>
        /// <param name="Y">position (en pxl) Y de la première frame de l'animation</param>
        /// <param name="Width">Largeur d'une frame</param>
        /// <param name="Height">Hauteur d'une frame</param>
        /// <param name="Frames">Nombre de frame</param>
        /// <param name="FrameLength">temps d'apparition en seconde d'une frame (0,1f est pas mal)</param>
        public void AddAnimation(string Name, int X, int Y, int Width, int Height, int Frames, float FrameLength)
        {
            faAnimations.Add(Name, new FrameAnimation(X, Y, Width, Height, Frames, FrameLength));
        }

        /// <summary>
        /// Ajoute une animation à l'objet en se basant sur sa spriteSheet, avec possibilité d'en désigner une suivante
        /// </summary>
        /// <param name="Name">Nom de l'animation</param>
        /// <param name="X">position (en pxl) X de la première frame de l'animation</param>
        /// <param name="Y">position (en pxl) Y de la première frame de l'animation</param>
        /// <param name="Width">Largeur d'une frame</param>
        /// <param name="Height">Hauteur d'une frame</param>
        /// <param name="Frames">Nombre de frame</param>
        /// <param name="FrameLength">temps d'apparition en seconde d'une frame (0,1f est pas mal)</param>
        /// <param name="NextAnimation">nom de la prochaine animation (facultatif)</param>
        public void AddAnimation(string Name, int X, int Y, int Width, int Height, int Frames, float FrameLength, string NextAnimation)
        {
            faAnimations.Add(Name, new FrameAnimation(X, Y, Width, Height, Frames, FrameLength, NextAnimation));
        }

        /// <summary>
        /// Cherche une animation dans le dico. Celle ci doit avoir été définie au préalable
        /// </summary>
        /// <param name="Name">nom de l'animation</param>
        /// <returns></returns>
        public FrameAnimation GetAnimationByName(string Name)
        {
            if (faAnimations.ContainsKey(Name))
            {
                return faAnimations[Name];
            }
            else
            {
                return null;
            }
        }
        #endregion

        #region Update and Draw

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gametime"></param>
        /// <param name="position">La position du sprite par son centre</param>
        /// <param name="rotation">l'angle de rotation</param>
        /// <param name="mouvement"></param>
        public void Update(GameTime gametime, Vector2 position, float rotation, bool mouvement)
        {

            //gestion de l'animation
            if (mouvement)
                animating = true;
            else 
                animating = false;

            if (this.AnimationImmobile)
            {
                animating = true;
            }

            if (animating && IsSpriteSheet)
            {
                    //DEBUG
                    // If there is not a currently active animation
                    if (CurrentFrameAnimation == null)
                    {
                        //DEBUG
                        // Make sure we have an animation associated with this sprite
                        if (faAnimations.Count > 0)
                        {
                            //DEBUG
                            // Set the active animation to the first animation
                            // associated with this sprite
                            string[] sKeys = new string[faAnimations.Count];
                            faAnimations.Keys.CopyTo(sKeys, 0);
                            currentAnimation = sKeys[0];
                            //DEBUG
                        }
                        else
                        {
                            return;
                        }
                    }
                    // Run the Animation's update method
                    CurrentFrameAnimation.Update(gametime);
                   
                    //maj de la taille du sprite en fonction de l'anime
                    spriteWidth = CurrentFrameAnimation.FrameWidth;
                    spriteHeight = CurrentFrameAnimation.FrameHeight;
                    spriteOrigin = new Vector2(spriteWidth / 2, spriteHeight / 2);
                    
                    // Check to see if there is a "followup" animation named for this animation
                    if (!String.IsNullOrEmpty(CurrentFrameAnimation.NextAnimation))
                    {

                        // If there is, see if the currently playing animation has
                        // completed a full animation loop
                        if (CurrentFrameAnimation.PlayCount > 0)
                        {
                            // If it has, set up the next animation
                            CurrentAnimation = CurrentFrameAnimation.NextAnimation;
                        }
                    }
                }
            //maj des coordonnées du sprite
            this.position = position;
            spriteRotation = rotation;
        }

        /// <summary>
        /// Affiche le sprite à la bonne position,orientation et échelle
        /// </summary>
        /// <param name="spriteBatch">Le spriteBatch dans lequel l'image est affichée</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            if (spriteSheet)
            {
                
                if (Flip)
                {
                    spriteBatch.Draw(image, position, CurrentFrameAnimation.FrameRectangle, colorTint, spriteRotation, spriteOrigin, scale, SpriteEffects.FlipHorizontally, layerDepth);
                }
                else
                {
                    spriteBatch.Draw(image, position, CurrentFrameAnimation.FrameRectangle, colorTint, spriteRotation, spriteOrigin, scale, SpriteEffects.None, layerDepth);
                }
            }
            else
            {
                if (Flip)
                {
                    spriteBatch.Draw(image, position, null, colorTint, spriteRotation, spriteOrigin, scale, SpriteEffects.FlipHorizontally, layerDepth);
                }
                else
                {
                    spriteBatch.Draw(image, position, null, colorTint, spriteRotation, spriteOrigin, scale, SpriteEffects.None, layerDepth);
                }
            }  
        }

        #endregion
    }
}
